package top.ieei.demo.ftp.client;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import sun.net.ftp.FtpClient;
import sun.net.ftp.FtpProtocolException;

public class FtpUtil
{

    private String ip = "";

    private String username = "";

    private String password = "";

    private int port = -1;

    private String path = "";

    FtpClient ftpClient = null;

    OutputStream os = null;

    FileInputStream is = null;

    public FtpUtil(String serverIP, String username, String password)
    {
        this.ip = serverIP;
        this.username = username;
        this.password = password;
    }

    public FtpUtil(String serverIP, int port, String username, String password)
    {
        this.ip = serverIP;
        this.username = username;
        this.password = password;
        this.port = port;
    }

    /**
     * 连接ftp服务器
     * 
     * @throws IOException
     */
    public boolean connectServer()
    {
        ftpClient = FtpClient.create();
        SocketAddress address = null;
        try
        {
            if (this.port != -1)
            {
            	address = new InetSocketAddress(this.ip, this.port);
                ftpClient.connect(address);
            }
            else
            {
            	address = new InetSocketAddress(this.ip, 21);
            	ftpClient.connect(address);
            }
            ftpClient.login(this.username, this.password.toCharArray());
            if (this.path.length() != 0)
            {
                ftpClient.changeDirectory(this.path);// path是ftp服务下主目录的子目录
            }
            ftpClient.setBinaryType();// 用2进制上传、下载
            System.out.println("已登录到\"" + ftpClient.getWorkingDirectory() + "\"目录");
            return true;
        }
        catch (FtpProtocolException e)
        {
            e.printStackTrace();
            return false;
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 断开与ftp服务器连接
     * 
     * @throws IOException
     */
    public boolean closeServer()
    {
        try
        {
            if (is != null)
            {
                is.close();
            }
            if (os != null)
            {
                os.close();
            }
            if (ftpClient != null)
            {
                ftpClient.close();
            }
            System.out.println("已从服务器断开");
            return true;
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 检查文件夹在当前目录下是否存在
     * 
     * @param dir
     * @return
     */
    private boolean isDirExist(String dir)
    {
        String pwd = "";
        try
        {
            pwd = ftpClient.getWorkingDirectory();
            ftpClient.changeDirectory(dir);
            ftpClient.changeDirectory(pwd);
        }
        catch (Exception e)
        {
            return false;
        }
        return true;
    }

    /**
     * 在当前目录下创建文件夹
     * 
     * @param dir
     * @return
     * @throws Exception
     */
    private boolean createDir(String dir)
    {
        try
        {
            ftpClient.setAsciiType();
            StringTokenizer s = new StringTokenizer(dir, "/"); // sign
            s.countTokens();
            String pathName = ftpClient.getWorkingDirectory();
            while (s.hasMoreElements())
            {
                pathName = pathName + "/" + (String) s.nextElement();
                try
                {
                    ftpClient.makeDirectory(pathName);
                }
                catch (Exception e)
                {
                    e = null;
                    return false;
                }
            }
            ftpClient.setBinaryType();
            return true;
        }
        catch (IOException e1)
        {
            e1.printStackTrace();
            return false;
        }
		catch (FtpProtocolException e)
		{
			e.printStackTrace();
			return false;
		}
    }

    /**
     * ftp上传 如果服务器段已存在名为filename的文件夹，该文件夹中与要上传的文件夹中同名的文件将被替换
     * 
     * @param filename 要上传的文件（或文件夹）名
     * @return
     * @throws Exception
     */
    public boolean upload(String filename)
    {
        String newname = "";
        if (filename.indexOf("/") > -1)
        {
            newname = filename.substring(filename.lastIndexOf("/") + 1);
        }
        else
        {
            newname = filename;
        }
        return upload(filename, newname);
    }

    /**
     * ftp上传 如果服务器段已存在名为newName的文件夹，该文件夹中与要上传的文件夹中同名的文件将被替换
     * 
     * @param fileName 要上传的文件（或文件夹）名
     * @param newName 服务器段要生成的文件（或文件夹）名
     * @return
     */
    public boolean upload(String fileName, String newName)
    {
        try
        {
            String savefilename = new String(fileName.getBytes("ISO-8859-1"), "GBK");
            File file_in = new File(savefilename);// 打开本地待长传的文件
            if (!file_in.exists())
            {
                throw new Exception("此文件或文件夹[" + file_in.getName() + "]有误或不存在!");
            }
            if (file_in.isDirectory())
            {
                upload(file_in.getPath(), newName, ftpClient.getWorkingDirectory());
            }
            else
            {
                uploadFile(file_in.getPath(), newName);
            }

            if (is != null)
            {
                is.close();
            }
            if (os != null)
            {
                os.close();
            }
            return true;
        }
        catch (Exception e)
        {
            e.printStackTrace();
            System.err.println("Exception e in Ftp upload(): " + e.toString());
            return false;
        }
        finally
        {
            try
            {
                if (is != null)
                {
                    is.close();
                }
                if (os != null)
                {
                    os.close();
                }
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }

    /**
     * 真正用于上传的方法
     * 
     * @param fileName
     * @param newName
     * @param path
     * @throws Exception
     */
    private void upload(String fileName, String newName, String path) throws Exception
    {
        String savefilename = new String(fileName.getBytes("ISO-8859-1"), "GBK");
        File file_in = new File(savefilename);// 打开本地待长传的文件
        if (!file_in.exists())
        {
            throw new Exception("此文件或文件夹[" + file_in.getName() + "]有误或不存在!");
        }
        if (file_in.isDirectory())
        {
            if (!isDirExist(newName))
            {
                createDir(newName);
            }
            ftpClient.changeDirectory(newName);
            File sourceFile[] = file_in.listFiles();
            for (int i = 0; i < sourceFile.length; i++)
            {
                if (!sourceFile[i].exists())
                {
                    continue;
                }
                if (sourceFile[i].isDirectory())
                {
                    this.upload(sourceFile[i].getPath(), sourceFile[i].getName(), path + "/" + newName);
                }
                else
                {
                    this.uploadFile(sourceFile[i].getPath(), sourceFile[i].getName());
                }
            }
        }
        else
        {
            uploadFile(file_in.getPath(), newName);
        }
        ftpClient.changeDirectory(path);
    }

    /**
     * upload 上传文件
     * 
     * @param filename 要上传的文件名
     * @param newname 上传后的新文件名
     * @return -1 文件不存在 >=0 成功上传，返回文件的大小
     * @throws Exception
     */
    public long uploadFile(String filename, String newname) throws Exception
    {
        long result = 0;
        OutputStream os = null;
        FileInputStream is = null;
        try
        {
            java.io.File file_in = new java.io.File(filename);
            if (!file_in.exists())
                return -1;
            os = ftpClient.putFileStream(newname);
            result = file_in.length();
            is = new FileInputStream(file_in);
            byte[] bytes = new byte[1024];
            int c;
            while ((c = is.read(bytes)) != -1)
            {
                os.write(bytes, 0, c);
            }
        }
        finally
        {
            if (is != null)
            {
                is.close();
            }
            if (os != null)
            {
                os.close();
            }
        }
        return result;
    }

    /**
     * 从ftp下载文件到本地
     * 
     * @param filename 服务器上的文件名
     * @param newfilename 本地生成的文件名
     * @return
     * @throws Exception
     */
    public long downloadFile(String filename, String newfilename)
    {
        long result = 0;
        InputStream is = null;
        FileOutputStream os = null;
        try
        {
            is = ftpClient.getFileStream(filename);
            java.io.File outfile = new java.io.File(newfilename);
            os = new FileOutputStream(outfile);
            byte[] bytes = new byte[1024];
            int c;
            while ((c = is.read(bytes)) != -1)
            {
                os.write(bytes, 0, c);
                result = result + c;
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
		catch (FtpProtocolException e)
		{
			e.printStackTrace();
		}
        finally
        {
            try
            {
                if (is != null)
                {
                    is.close();
                }
                if (os != null)
                {
                    os.close();
                }
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 取得相对于当前连接目录的某个目录下所有文件列表
     * 
     * @param path
     * @return
     */
    public List<String> getFileList(String path)
    {
        List<String> list = new ArrayList<String>();
        BufferedReader br;
        try
        {
        	br = new BufferedReader(new InputStreamReader(ftpClient.nameList(this.path + path)));
            String filename = "";
            while ((filename = br.readLine()) != null)
            {
                list.add(filename);
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
		catch (FtpProtocolException e)
		{
			e.printStackTrace();
		}
        return list;
    }
    public void delete_file(FtpUtil fu,String url) throws Exception {
    	ftpClient.deleteFile(url);
    }
    public static void main(String[] args)
    {
        FtpUtil ftp = new FtpUtil("133.224.202.2", "tstbill", "tstbill");
        ftp.connectServer();
        boolean result = ftp.upload("C:/test_why", "test_why/test");
        System.out.println(result ? "上传成功！" : "上传失败！");
        List<String> list = ftp.getFileList("test_why/test");
        for (int i = 0; i < list.size(); i++)
        {
            String name = list.get(i).toString();
            System.out.println(name);
        }
        ftp.closeServer();
        /**
         * FTP远程命令列表 USER PORT RETR ALLO DELE SITE XMKD CDUP FEAT PASS PASV STOR
         * REST CWD STAT RMD XCUP OPTS ACCT TYPE APPE RNFR XCWD HELP XRMD STOU
         * AUTH REIN STRU SMNT RNTO LIST NOOP PWD SIZE PBSZ QUIT MODE SYST ABOR
         * NLST MKD XPWD MDTM PROT
         * 在服务器上执行命令,如果用sendServer来执行远程命令(不能执行本地FTP命令)的话，所有FTP命令都要加上\r\n
         * ftpclient.sendServer("XMKD /test/bb\r\n"); //执行服务器上的FTP命令
         * ftpclient.readServerResponse一定要在sendServer后调用
         * nameList("/test")获取指目录下的文件列表 XMKD建立目录，当目录存在的情况下再次创建目录时报错 XRMD删除目录
         * DELE删除文件
         */
    }
    /*
     * sun.net.ftp.FtpClient.，该类库主要提供了用于建立FTP连接的类。利用这些类的方法，编程人员可以远程登录到FTP服务器，
     * 列举该服务器上的目录
     * ，设置传输协议，以及传送文件。FtpClient类涵盖了几乎所有FTP的功能，FtpClient的实例变量保存了有关建立"代理"
     * 的各种信息。下面给出了这些实例变量：
     * 
     * 　　public static boolean useFtpProxy
     * 　　这个变量用于表明FTP传输过程中是否使用了一个代理，因此，它实际上是一个标记，此标记若为TRUE，表明使用了一个代理主机。
     * 
     * 　　public static String ftpProxyHost
     * 　　此变量只有在变量useFtpProxy为TRUE时才有效，用于保存代理主机名。
     * 
     * 　　public static int ftpProxyPort
     * 　　此变量只有在变量useFtpProxy为TRUE时才有效，用于保存代理主机的端口地址。
     * 
     * 　　FtpClient有三种不同形式的构造函数，如下所示：
     * 
     * 　　1、public FtpClient(String hostname,int port)
     * 　　　此构造函数利用给出的主机名和端口号建立一条FTP连接。
     * 
     * 　　2、public FtpClient(String hostname) 　　此构造函数利用给出的主机名建立一条FTP连接，使用默认端口号。
     * 
     * 　　3、FtpClient()
     * 　　此构造函数将创建一FtpClient类，但不建立FTP连接。这时，FTP连接可以用openServer方法建立。
     * 
     * 　　一旦建立了类FtpClient，就可以用这个类的方法来打开与FTP服务器的连接。
     * 类ftpClient提供了如下两个可用于打开与FTP服务器之间的连接的方法。
     * 
     * 　　public void openServer(String hostname)
     * 　　这个方法用于建立一条与指定主机上的FTP服务器的连接，使用默认端口号。
     * 
     * 　　public void openServer(String host,int port)
     * 　　这个方法用于建立一条与指定主机、指定端口上的FTP服务器的连接。
     * 
     * 　　打开连接之后，接下来的工作是注册到FTP服务器。这时需要利用下面的方法。
     * 
     * 　　public void login(String username，String password)
     * 　　此方法利用参数username和password登录到FTP服务器
     * 。使用过Intemet的用户应该知道，匿名FTP服务器的登录用户名为anonymous，密码一般用自己的电子邮件地址。
     * 
     * 　　下面是FtpClient类所提供的一些控制命令。
     * 
     * 　　public void cd(String
     * remoteDirectory)：该命令用于把远程系统上的目录切换到参数remoteDirectory所指定的目录。 　　public void
     * cdUp()：该命令用于把远程系统上的目录切换到上一级目录。 　　public String pwd()：该命令可显示远程系统上的目录状态。
     * 　　public void binary()：该命令可把传输格式设置为二进制格式。 　　public void
     * ascii()：该命令可把传输协议设置为ASCII码格式。 　　public void rename(String string，String
     * string1)：该命令可对远程系统上的目录或者文件进行重命名操作。
     * 
     * 　　除了上述方法外，类FtpClient还提供了可用于传递并检索目录清单和文件的若干方法。这些方法返回的是可供读或写的输入、输出流。
     * 下面是其中一些主要的方法。
     * 
     * 　　public TelnetInputStream list() 　　返回与远程机器上当前目录相对应的输入流。
     * 
     * 　　public TelnetInputStream get(String filename)
     * 　　获取远程机器上的文件filename，借助TelnetInputStream把该文件传送到本地。
     * 
     * 　　public TelnetOutputStream put(String filename)
     * 　　以写方式打开一输出流，通过这一输出流把文件filename传送到远程计算机
     */

}